1 /*
2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021
3 License:   [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License].
4 Authors: Marcelo S. N. Mancini
5 
6 	Copyright Marcelo S. N. Mancini 2018 - 2021.
7 Distributed under the CC BY-4.0 License.
8    (See accompanying file LICENSE.txt or copy at
9 	https://creativecommons.org/licenses/by/4.0/
10 */
11 module hip.hipaudio.audio;
12 
13 public import hip.hipaudio.audiosource;
14 public import hip.api.audio;
15 import hip.config.audio;
16 
17 //Backends
18 
19 
20 
21 import hip.audio_decoding.audio;
22 import hip.math.utils:getClosestMultiple;
23 import hip.util.reflection;
24 import hip.error.handler;
25 
26 
27 /** 
28  * This is an interface that should be created only once inside the application.
29  *  Every audio function is global, meaning that every AudioSource will refer to the player
30  */
31 public interface IHipAudioPlayer
32 {
33     //LOAD RELATED
34     public bool play_streamed(AHipAudioSource src);
35     public IHipAudioClip getClip();
36     public IHipAudioClip loadStreamed(string path, uint chunkSize);
37     public void updateStream(AHipAudioSource source);
38     public AHipAudioSource getSource(bool isStreamed);
39 
40     public void onDestroy();
41     public void update();
42 }
43 
44 class HipAudio
45 {
46     public static bool initialize(HipAudioImplementation implementation = HipAudioImplementation.OpenAL,
47     bool hasProAudio = false,
48     bool hasLowLatencyAudio = false,
49     int  optimalBufferSize = 4096,
50     int optimalSampleRate = 44_100)
51     {
52         ErrorHandler.startListeningForErrors("HipremeAudio initialization");
53         _hasInitializedAudio = true;
54         HipAudio.is3D = is3D;
55         audioInterface = getAudioInterface(implementation);
56         HipAudio.hasProAudio        = hasProAudio;
57         HipAudio.hasLowLatencyAudio = hasLowLatencyAudio;
58         HipAudio.optimalBufferSize  = optimalBufferSize;
59         HipAudio.optimalSampleRate  = optimalSampleRate;
60         return ErrorHandler.stopListeningForErrors();
61     }
62     @ExportD static bool pause(AHipAudioSource src)
63     {
64         src.isPlaying = false;
65         return false;
66     }
67     @ExportD static bool play_streamed(AHipAudioSource src)
68     {
69         audioInterface.play_streamed(src);
70         src.isPlaying = true;
71         return false;
72     }
73     @ExportD static IHipAudioClip getClip(){return audioInterface.getClip();}
74 
75     /**
76     *   Loads a file from disk, sets the chunkSize for streaming and does one decoding frame
77     */
78     @ExportD static IHipAudioClip loadStreamed(string path, uint chunkSize = ushort.max+1)
79     {
80         chunkSize = getClosestMultiple(optimalBufferSize, chunkSize);
81         IHipAudioClip buf = audioInterface.loadStreamed(path, chunkSize);
82         return buf;
83     }
84 
85     @ExportD static void updateStream(HipAudioSource source)
86     {
87         audioInterface.updateStream(source);
88     }
89     @ExportD static AHipAudioSource getSource(bool isStreamed = false, IHipAudioClip clip = null)
90     {
91         if(isStreamed) ErrorHandler.assertExit(clip !is null, "Can't get streamed source without any buffer");
92         HipAudioSource ret = cast(HipAudioSource)audioInterface.getSource(isStreamed);
93         if(clip)
94             ret.clip = clip;
95         return ret;
96     }
97     @ExportD static void onDestroy()
98     {
99         if(audioInterface !is null)
100             audioInterface.onDestroy();
101         audioInterface = null;
102     }
103 
104     static void update()
105     {
106         if(audioInterface !is null)
107             audioInterface.update();
108     }
109 
110     private static IHipAudioPlayer getAudioInterface(HipAudioImplementation impl,
111     bool hasProAudio = false,
112     bool hasLowLatencyAudio = false,
113     int  optimalBufferSize = 4096,
114     int optimalSampleRate = 44_100)
115     {
116         import hip.console.log;
117         final switch(impl)
118         {
119             case HipAudioImplementation.WebAudio:
120             {
121                 static if(HasWebAudio)
122                 {
123                     import hip.hipaudio.backend.webaudio.player;
124                     return new HipWebAudioPlayer(AudioConfig.musicConfig);
125                 }
126                 else
127                 {
128                     loglnWarn("Tried to use WebAudio implementation, but not in WebAssembly. No audio available");
129                     goto case HipAudioImplementation.Null;
130                 }
131             }
132             case HipAudioImplementation.OpenSLES:
133                 static if(HasOpenSLES)
134                 {
135                     import hip.hipaudio.backend.opensles.player;
136                     return new HipOpenSLESAudioPlayer(AudioConfig.androidConfig,
137                     hasProAudio,
138                     hasLowLatencyAudio,
139                     optimalBufferSize,
140                     optimalSampleRate);
141                     break;
142                 }
143             case HipAudioImplementation.XAudio2:
144                 static if(HasXAudio2)
145                 {
146                     import hip.hipaudio.backend.xaudio.player;
147                     loglnInfo("Initializing XAudio2 with audio config ", AudioConfig.musicConfig);
148                     return new HipXAudioPlayer(AudioConfig.musicConfig);
149                 }
150                 else 
151                 {
152                     loglnWarn("Tried to use XAudio2 implementation, but no XAudio2 version was provided. OpenAL will be used instead");
153                     goto case HipAudioImplementation.OpenAL;
154                 }
155             case HipAudioImplementation.AVAudioEngine:
156             {
157                 static if(HasAVAudioEngine)
158                 {
159                     import hip.hipaudio.backend.avaudio.player;
160                     return new HipAVAudioPlayer(AudioConfig.androidConfig);
161                 }
162                 else
163                 {
164                     loglnWarn("Tried to use AVAudioEngine implementation, but no AVAudioEngine found. OpenAL will be used instead");
165                     goto case HipAudioImplementation.OpenAL;
166                 }
167             }
168             case HipAudioImplementation.OpenAL:
169             {
170                 static if(HasOpenAL)
171                 {
172                     import hip.hipaudio.backend.openal.player;
173                     //Please note that OpenAL HRTF(spatial sound) only works with Mono Channel
174                     return new HipOpenALAudioPlayer(AudioConfig.musicConfig);
175                 }
176                 else
177                 {
178                     loglnWarn("Tried to use OpenAL implementation, but no OpenAL version was provided. No audio available.");
179                     goto case HipAudioImplementation.Null;
180                 }
181             }
182             case HipAudioImplementation.Null:
183             {
184                 import hip.hipaudio.backend.nullaudio;
185                 loglnWarn("No AudioInterface was found. Using NullAudio");
186                 return new HipNullAudio();
187             }
188         }
189     }
190 
191 
192    
193     protected __gshared bool hasProAudio;
194     protected __gshared bool hasLowLatencyAudio;
195     protected __gshared int  optimalBufferSize;
196     protected __gshared int  optimalSampleRate;
197     private   __gshared bool is3D;
198     private   __gshared uint activeSources;
199 
200     __gshared IHipAudioPlayer audioInterface;
201 
202     //Debug vars
203     private __gshared bool _hasInitializedAudio = false;
204     public bool hasInitializedAudio() => _hasInitializedAudio;
205 }